package com.jade.journal.appender;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import com.jade.framework.base.util.DataUtils;
import com.jade.journal.JournalEvent;
import com.jade.journal.desc.AppenderDesc;
import com.jade.journal.desc.JournalDesc;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;

/**
 * 文件方式追加器。
 *
 * @author sky
 * @version 1.0 14-9-22 下午1:12
 */
public class FileAppender extends StreamAppender
{
    public static final String TYPE = "file";
    protected String fileName;
    protected File file;
    private int limit = 0;

    //protected JournalOutputStream out;
    private CountingOutputStream cos = null;

    public FileAppender(JournalDesc desc, AppenderDesc appenderDesc)
    {
        super(TYPE, desc, appenderDesc);
    }

    public int getLimit()
    {
        return limit;
    }

    /**
     * 设置文件名
     *
     * @param file 文件
     */
    public void setFile(File file)
    {
        this.file = file;
        this.fileName = file.getName();
    }

    public void init(JournalEvent event)
    {
        super.init(event);
        limit = DataUtils.getInt(appenderDesc.getProperty("limit"), limit);

        if (file == null) {
            log.error("Appender file must be set!");
            return;
        }

        if (limit > 0) {
            this.file = generate();
        }

        FileOutputStream fos = null;
        try {
            boolean appendHeader = !file.exists();
            if (appendHeader) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            fos = new FileOutputStream(file.getAbsolutePath(), true);
            cos = new CountingOutputStream(fos);
            if (appendHeader) {
                format.formatHeader(event, cos, encoding);
            }
        }
        catch (Throwable t) {
            log.error("Can't init appender:", t);
            if (cos != null) {
                IOUtils.closeQuietly(cos);
            }
            else {
                IOUtils.closeQuietly(fos);
            }
        }
    }

    public synchronized void append(JournalEvent... events)
    {
        if (cos != null) {
            if (overLimit()) {
                try {
                    rotate(events[0]);
                }
                catch (IOException e) {
                    log.error("Can't rotate file:", e);
                    return;
                }
            }
            for (JournalEvent je : events) {
                je.setDesc(desc);
                try {
                    doAppend(je);
                }
                catch (IOException e) {
                    log.error("Append event exception", e);
                    return;
                }
            }
        }
        else {
            log.error("No file set");
        }
    }

    private void doAppend(JournalEvent event)
            throws IOException
    {
        format.format(event, cos, encoding);
    }

    public synchronized void close()
    {
        try {
            cos.flush();
            cos.close();
        }
        catch (Exception e) {
        }
        cos = null;
    }

    // Rotate the set of output files
    private void rotate(JournalEvent event)
            throws IOException
    {
        close();
        file = generate();
        init(event);
    }

    private String generate(int i)
    {
        int len = fileName.length() + 3;
        StringBuilder sb = new StringBuilder(len);
        sb.append(fileName).append('.');
        sb.append(i);
        return sb.toString();
    }

    private File generate()
    {
        if (limit <= 0) {
            return file;
        }
        else if (!file.exists() || file.length() < limit) {
            return file;
        }

        int unique = 1;
        String oldFileName = file.getName();
        if (oldFileName.length() > fileName.length()) {
            unique = DataUtils.getInt(oldFileName.substring(fileName.length()), 1);
        }

        String newFileName = null;
        File newFile = null;
        for (; ; unique++) {
            newFileName = generate(unique);
            newFile = new File(file.getParent(), newFileName);
            if (!newFile.exists() || newFile.length() < limit) {
                return newFile;
            }
        }
    }

    public boolean overLimit()
    {
        return (limit > 0 && cos.getCount() > limit);
    }
}